package com.hp.bon.sgw.threads;

import static com.hp.bon.sgw.Constants.LOG;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.dom4j.Element;
import org.dom4j.Node;

import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.bean.SysParamBean;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.domain.BlackListLog;
import com.hp.bon.sgw.domain.BlacklistRule;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.ocs.diameter.message.AVP;
import com.hp.ocs.diameter.message.AVP_Grouped;
import com.hp.ocs.diameter.message.AVP_UTF8String;
import com.hp.ocs.diameter.message.InvalidAVPLengthException;
import com.hp.ocs.diameter.message.Message;
import com.hp.ocs.diameter.message.dictionary.AvpType;

public class BlacklistMonitorThread extends TaskThread {

	private static BlacklistMonitorThread _instance;

  private ConcurrentHashMap<String, Date> blackList = new ConcurrentHashMap<String, Date>();

  private ConcurrentHashMap<String, UserStat> usrStatMap = new ConcurrentHashMap<String, UserStat>();

  private SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  private int blackCount;

	public static BlacklistMonitorThread getInstance() {
		if (_instance == null) {
			_instance = new BlacklistMonitorThread();
		}
		return _instance;
	}

	protected long sleepTime = 60000;// 检测周期

	/**
	 * 先执行父类的构造函数，再执行子类的构造函数
	 */
	public BlacklistMonitorThread() {
		setName("Task-BlacklistMonitor");
	}

	/**
	 * 收到业务请求后，检查是否是黑名单
	 * 
	 * @param callParam
	 * @return
	 */
	public String checkBlackList(CallParam callParam) {
		BlacklistRule rule = ConfigService.blacklistRuleMap.get(callParam.getCapability());
		if (rule == null) {
			return null;
		}
		Object request = callParam.getRequest();
		if (request == null) {
			return null;
		}
    	String key = getKey(request, rule.getKeyXmlTag(), rule.getKeyAvpPath());
		if (key == null) {
			return null;
		}
		Date expireDate = blackList.get(key);
		if (expireDate != null && expireDate.getTime() > callParam.getRecvReqTime())
			return key;
		return null;
	}

	/**
	 * 结束业务处理时，根据请求和应答的关键字，进行黑名单检查
	 * 
	 * @param callParam
	 */
	public void add(CallParam callParam) {

		BlacklistRule rule = ConfigService.blacklistRuleMap.get(callParam.getCapability());
		if (rule == null) {
			return;
		}
		Object request = callParam.getRequest();
		if (request == null) {
			return;
		}
    	String key = getKey(request, rule.getKeyXmlTag(), rule.getKeyAvpPath());
		if (key == null) {
			return;
		}

		if (rule.getRcMap().contains(callParam.getServiceResultCode())) {

			// 判断是否满足黑名单规则
			UserStat userStat = usrStatMap.get(key);
			if (userStat == null) {
				userStat = new UserStat(key);
				usrStatMap.put(key, userStat);
			}
			userStat.add(false);
			int errorNum = userStat.getErrorNum();
			if (errorNum >= this.blackCount) {
				// 如果满足黑名单
				if (!blackList.containsKey(userStat.key)) {
					Date blacklistExpireDate = new Date(System.currentTimeMillis() + SpringContext.getInstance().getConfigService().getIntValue(SysParamBean.KEY_BLACKLIST_DURATION, 3600) * 1000);
					blackList.put(userStat.key, blacklistExpireDate);
					BlackListLog log = new BlackListLog();
					log.setBlackKey(userStat.key);
					log.setStartTime(new Date());
					log.setEndTime(blacklistExpireDate);
					SpringContext.getInstance().getOuterDBDataService().saveToDB(log);
					LOG.warn("put user in blacklist:" + userStat.key + " ,failed count:" + errorNum + " ,expire time:" + TIME_FORMAT.format(blacklistExpireDate));
				}
			}
		}
	}

  static String getKey(Object request, String xmlTag, LinkedList<AvpType> avpPath)
  {
    String key = null;
    if ((request instanceof XmlMessage)) {
      if (xmlTag == null)
        return null;
      Element e = ((XmlMessage)request).getDataElement();
      Node keyNode = e.selectSingleNode(xmlTag);
      if (keyNode != null)
        key = keyNode.getStringValue();
    } else if ((request instanceof Message)) {
      Message m = (Message)request;
      AVP_Grouped parentGroup = null;
      AVP findAvp = null;
      try {
        if ((avpPath == null) || (avpPath.size() <= 0)) return null;
        for (Iterator it = avpPath.iterator(); it.hasNext(); ) {
          AvpType avpType = (AvpType)it.next();

          if (parentGroup == null)
            findAvp = m.find(avpType.getCode(), avpType.getVendorId());
          else {
            findAvp = parentGroup.find(avpType.getCode(), avpType.getVendorId());
          }
          if (findAvp == null)
            break;
          if (it.hasNext()) {
            parentGroup = new AVP_Grouped(findAvp);
          }
          else
            key = AVP_UTF8String.queryValue(findAvp);
        }
      }
      catch (InvalidAVPLengthException e1) {
        Constants.LOG.warn("InvalidAVPLengthException:", e1);
      }
    }
    return key;
  }
	/**
	 * 每次循环完成功能:1、统计请求结果码，检查是否满足黑名单；2检查黑名单的key是否已失效
	 */
	public void process() {
		this.blackCount = SpringContext.getInstance().getConfigService().getIntValue(SysParamBean.KEY_BLACKLIST_COUNT, 3);
		this.sleepTime = SpringContext.getInstance().getConfigService().getIntValue(SysParamBean.KEY_BLACKLIST_PERIOD, 60) * 1000;

		Iterator<Map.Entry<String, UserStat>> itor = usrStatMap.entrySet().iterator();
		Map.Entry<String, UserStat> entry = null;
		while (itor.hasNext()) {
			entry = itor.next();
			UserStat usrStat = entry.getValue();
			usrStat.reset();
			if (usrStat.getErrorNum() <= 0)
				itor.remove();
		}

		Iterator<Map.Entry<String, Date>> itor1 = blackList.entrySet().iterator();
		Map.Entry<String, Date> entry1 = null;
		while (itor1.hasNext()) {
			entry1 = itor1.next();
			// 如果已经过了有效期，则总黑名单中剔除
			if (entry1.getValue().getTime() < this.runTime) {
				LOG.warn("remove user out blacklist:" + entry1.getKey());
				itor1.remove();
			}
		}
		this.sleepTime = SpringContext.getInstance().getConfigService().getIntValue(SysParamBean.KEY_BLACKLIST_PERIOD, 60) * 1000;
	}

  public ConcurrentHashMap<String, Date> getBlackList()
  {
    return this.blackList;
  }

  public class UserStat
  {
    String key;
    Stat curStat = new Stat();
    Stat lastStat = new Stat();

    public UserStat(String key2)
    {
      this.key = key2;
    }

    public void add(boolean falg)
    {
      if (falg)
        this.curStat.successNum += 1;
      else
        this.curStat.errorNum += 1;
    }

    public void reset()
    {
      this.lastStat = this.curStat;
      this.curStat = new Stat();
    }

    public int getErrorNum() {
      return this.curStat.errorNum + this.lastStat.errorNum;
    }

    public class Stat
    {
      int successNum;
      int errorNum;

      public Stat()
      {
      }

      public String toString()
      {
        return this.successNum + "-" + this.errorNum;
      }
    }
  }
}